package fons.model.ontology;

import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

public class ValueSet {
	private final Map<Value, Double> valueMap;

	private ValueSet(Map<Value, Double> valueMap) {
		this.valueMap = valueMap;
	}
	
	public ValueSet(Collection<Value> values) {
		this.valueMap = Maps.newHashMap();
		for (Value value: values) {
			this.valueMap.put(value, value.getValue());
		}
	}

	public Value getOneOrNone (Dimension... discriminators) {
		return getOneOrNone(Lists.newArrayList(discriminators));
	}
	
	public Value getOneOrNone (Collection<Dimension> discriminators) {
		Value ret = null;
		for (Value value: valueMap.keySet()) {
			if (value.hasAllOf(discriminators)) {
				if (ret != null) return null;
				ret = value;
			}
		}
		return ret;
	}

	public Double getMappedValue (Value value) {
		if (value == null) return Double.NaN;
		Double d = valueMap.get(value);
		if (d == null) return Double.NaN;
		return d;
	}

	public ValueSet map (Mapper mapper) {
		ValueSet tmp = new ValueSet(mapper.map(valueMap));
		return tmp;		
	}

	public static interface Mapper {
		public Map<Value, Double> map (Map<Value, Double> map);
	}
	
	public static class Log10 implements Mapper {
		@Override
		public Map<Value, Double> map(Map<Value, Double> map) {
			Map<Value, Double> retVal = Maps.newHashMap();
			
			for (Entry<Value, Double> entry: map.entrySet()) {
				retVal.put(entry.getKey(), Math.log10(1 + Math.abs(entry.getValue())));
			}
			
			return retVal;
		}
	}

	public static class Normalizer implements Mapper {
		private final double MIN, MAX;
		private final boolean relative;

		public Normalizer() {
			this(0, 1);
		}

		public Normalizer (double min, double max) {
			this(min, max, false);
		}

		public Normalizer (double min, double max, boolean relative) {
			this.MIN = min;
			this.MAX = max;
			this.relative = relative;
		}

		@Override
		public Map<Value, Double> map(Map<Value, Double> map) {
			double max = Double.NaN;
			double min = Double.NaN;
			Map<Value, Double> retVal = Maps.newHashMap();

			for (final Double v: map.values()) {
				if (Double.isNaN(max) && !Double.isNaN(v)) {
					max = v;
					min = v;

				} else if (!Double.isNaN(v)) {
					if (v > max) max = v;
					else if (v < min) min = v;
				}
			}
			
			if (!relative && min > 0) min = 0;
			
			for (Entry<Value, Double> entry: map.entrySet()) {
				final Value value = entry.getKey();
				final double x = entry.getValue();
				retVal.put(value,  MIN + ((x-min)/(max-min)) * (MAX-MIN));
			}
			return retVal;
		}

	}
}
